# harmonyOS_checktoolpage.py
# 2025.8.2 (重构版 - UI交互层)
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
HarmonyOS 开发环境一键检测 & 自动打包工具的主界面（浅色主题）
"""
import logging
import os
import threading
import time
import tkinter as tk
from tkinter import ttk, filedialog, messagebox

# --- 颜色常量 ---
BG_LIGHT = "#ffffff"
ACCENT = "#2563eb"
TEXT_DARK = "#1e293b"
TEXT_MUTED = "#64748b"
OK_GREEN = "#137333"
ERROR_RED = "#d93025"

# --- 导入重构后的逻辑函数 ---
# 假定 harmonyOS_packing.py 和 harmonyOS_check.py 都在推荐的、分离的模块中
from packingtools.harmonyOS_packing import (
    is_valid_project_root,
    run_packing_process,
    get_connected_devices,
    install_hap_package,
    check_command
)
from checktools.harmonyOS_check import (
    get_devecostudio_info,
    get_sdk_version_from_deveco,
    get_environment_tools,
)


class HarmonyOS_CheckToolPage(tk.Frame):
    """主界面，负责所有UI展示和用户交互"""

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # --- 变量 ---
        self.project_root = tk.StringVar()
        self._alive = True
        self._last_check_time = 0

        # --- 字体 ---
        self.title_font = ("Segoe UI", 14, "bold")
        self.subtitle_font = ("Segoe UI", 10)

        self._setup_widgets()
        self.after(300, self.check_toolchain)

    def destroy(self):
        self._alive = False
        super().destroy()

    # ------------------------------------------------------------------
    # 界面构建 (Setup Widgets)
    # ------------------------------------------------------------------
    def _setup_widgets(self):
        main = ttk.Frame(self)
        main.pack(fill=tk.BOTH, expand=True)

        # 头部
        header = ttk.Frame(main)
        header.pack(fill=tk.X, pady=(0, 10))
        ttk.Label(header, text="HarmonyOS 开发环境检测 & 打包工具", font=self.title_font, foreground=ACCENT).pack(
            side=tk.LEFT)
        self.time_lbl = ttk.Label(header, text="", font=self.subtitle_font, foreground=TEXT_MUTED)
        self.time_lbl.pack(side=tk.RIGHT)

        # 项目配置区
        path_frm = ttk.LabelFrame(main, text="项目配置", padding=10)
        path_frm.pack(fill=tk.X, pady=(0, 10))
        path_frm.columnconfigure(1, weight=1)
        ttk.Label(path_frm, text="项目根目录：").grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
        ttk.Entry(path_frm, textvariable=self.project_root).grid(row=0, column=1, sticky=tk.EW)
        ttk.Button(path_frm, text="浏览...", command=self.browse_project_root).grid(row=0, column=2, padx=5)
        ttk.Button(path_frm, text="编译打包", style="Accent.TButton", command=self.build_project).grid(row=0, column=3,
                                                                                                       padx=(5, 0))

        # 功能选项卡 (Notebook)
        nb = ttk.Notebook(main)
        nb.pack(fill=tk.BOTH, expand=True)

        self.deco_tree = self._create_tree_tab(nb, "DevEco Studio")
        self.env_tree = self._create_tree_tab(nb, "环境依赖")

        # HDC 设备页特殊处理
        hdc_tab = ttk.Frame(nb)
        nb.add(hdc_tab, text="HDC 设备")
        self.hdc_tree = ttk.Treeview(hdc_tab, show="tree", selectmode="browse", columns=("sn",))
        self.hdc_tree.column("#0", width=400, anchor=tk.W)
        self.hdc_tree.column("sn", width=0, stretch=tk.NO)  # 隐藏SN列
        self.hdc_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.hdc_tree.bind("<Button-3>", self._show_hdc_context_menu)

        # 底部操作按钮
        btn_frm = ttk.Frame(main)
        btn_frm.pack(fill=tk.X, pady=(10, 0))
        ttk.Button(btn_frm, text="一键检测", style="Accent.TButton", command=self.check_toolchain).pack(side=tk.LEFT,
                                                                                                        padx=(0, 10))
        ttk.Button(btn_frm, text="导出报告", command=self.export_report).pack(side=tk.LEFT)

        self._configure_styles()

    def _create_tree_tab(self, notebook, text):
        """辅助函数，用于创建标准的Treeview选项卡"""
        tab = ttk.Frame(notebook)
        notebook.add(tab, text=text)
        tree = ttk.Treeview(tab, show="tree", selectmode="none")
        tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        return tree

    def _configure_styles(self):
        """配置所有 ttk 控件的样式"""
        style = ttk.Style()
        style.configure("Accent.TButton", font=("Segoe UI", 10, "bold"))

        for tree in [self.deco_tree, self.env_tree, self.hdc_tree]:
            tree.tag_configure("ok", foreground=OK_GREEN)
            tree.tag_configure("error", foreground=ERROR_RED)
            tree.tag_configure("version", foreground=TEXT_MUTED)
            tree.tag_configure("path", foreground=TEXT_MUTED)
            tree.tag_configure("requirement", foreground="#ea4335")  # 特殊要求用橙色
        self.deco_tree.tag_configure("sdk", foreground=ACCENT)  # DevEco SDK用主题色

    # ------------------------------------------------------------------
    # UI 事件处理 (Event Handlers)
    # ------------------------------------------------------------------
    def browse_project_root(self):
        path = filedialog.askdirectory(title="请选择 HarmonyOS 项目根目录")
        if path: self.project_root.set(path)

    def check_toolchain(self):
        """防抖地触发异步检查"""
        if time.time() - self._last_check_time < 3: return
        self._last_check_time = time.time()

        if self.time_lbl.winfo_exists(): self.time_lbl.config(text="检测中...")
        threading.Thread(target=self._async_check_all, daemon=True).start()

    def _async_check_all(self):
        """在后台线程中执行所有检查，然后调度UI更新"""
        if not self._alive: return
        # 依次调用各个填充方法，它们内部会处理好线程和UI调度
        self._fill_deco_tree()
        self._fill_env_tree()
        self._fill_hdc_tree()
        if self._alive and self.time_lbl.winfo_exists():
            now = time.strftime('%Y-%m-%d %H:%M:%S')
            self.after(0, lambda: self.time_lbl.config(text=f"最后检测时间: {now}"))

    def schedule_hdc_refresh(self):
        """由外部(UsbWatcher)调用的线程安全方法，用于刷新HDC设备列表"""
        if self._alive and self.winfo_exists():
            self.after(0, self._fill_hdc_tree)

    # ------------------------------------------------------------------
    # Treeview 填充逻辑
    # ------------------------------------------------------------------
    def _fill_deco_tree(self):
        info = get_devecostudio_info()
        sdk = get_sdk_version_from_deveco(
            info.get("InstallLocation") if isinstance(info, dict) else None
        )

        def update_ui():
            if not self._alive:
                return
            tree = self.deco_tree
            try:
                if not tree.winfo_exists():
                    return
                self.safe_tree_delete(tree, *tree.get_children())

                if isinstance(info, dict):
                    self.safe_tree_insert(tree, "", "end", text=f"名称: {info.get('DisplayName', '未知')}")
                    self.safe_tree_insert(tree, "", "end", text=f"路径: {info.get('InstallLocation', '未知')}")
                    if sdk.get("error"):
                        self.safe_tree_insert(tree, "", "end", text=sdk["error"], tags=("error",))
                    else:
                        self.safe_tree_insert(
                            tree, "", "end", text=f"版本: {sdk.get('version', '未知')}", tags=("sdk",)
                        )
                        self.safe_tree_insert(
                            tree, "", "end", text=f"API: {sdk.get('apiVersion', '未知')}", tags=("sdk",)
                        )
                else:
                    self.safe_tree_insert(tree, "", "end", text=info, tags=("error",))
            except tk.TclError:
                pass

        self.after(0, update_ui)

    def _fill_env_tree(self):
        def update_single_tool(name, cmds, arg, extra, node_id):
            result, ver, path = check_command(cmds, arg, extra)
            self.after(0, _update_env_node, node_id, name, result, ver, path)

        def _update_env_node(node_id, name, result, version, path):
            if not self._alive:
                return
            tree = self.env_tree
            try:
                if not tree.winfo_exists() or not tree.exists(node_id):
                    return
                # 仅保留工具名
                tool_name = name.split()[0] if name else ""
                tree.item(node_id, text=tool_name)
                tag = "ok" if "✅" in result else "error"
                self.safe_tree_insert(tree, node_id, "end", text=result, tags=(tag,))
                self.safe_tree_insert(tree, node_id, "end", text=version, tags=(tag,))
                self.safe_tree_insert(tree, node_id, "end", text=path, tags=(tag,))
            except tk.TclError:
                pass

        def update_ui():
            if not self._alive:
                return
            tree = self.env_tree
            try:
                if not tree.winfo_exists():
                    return
                self.safe_tree_delete(tree, *tree.get_children())
                root = self.safe_tree_insert(tree, "", "end", text="命令行工具链检测", open=True)
                for tool in get_environment_tools():
                    name, *args = tool
                    node = self.safe_tree_insert(tree, root, "end", text=name)
                    cmds, arg, extra = args[0], args[1] if len(args) > 1 else "", args[2] if len(args) > 2 else ""
                    threading.Thread(
                        target=update_single_tool, args=(name, cmds, arg, extra, node), daemon=True
                    ).start()
            except tk.TclError:
                pass

        self.after(0, update_ui)

    def _update_env_node(self, node_id, name, result, version, path):
        if not self._alive or not self.env_tree.winfo_exists():
            return
        if not self.env_tree.exists(node_id):
            return

        # 只保留工具名称，去掉版本信息和其他细节
        tool_name = name.split()[0] if name else ""
        if "✅" in result:
            self.env_tree.item(node_id, text=tool_name)
            self.env_tree.insert(node_id, "end", text=result, tags=("ok",))
            self.env_tree.insert(node_id, "end", text=version, tags=("ok",))
            self.env_tree.insert(node_id, "end", text=path, tags=("ok",))

        else:
            self.env_tree.item(node_id, text=tool_name)
            self.env_tree.insert(node_id, "end", text=result, tags=("error",))

    def _fill_hdc_tree(self):
        def task():
            devices = get_connected_devices()
            self.after(0, update_ui, devices)

        def update_ui(devices):
            if not self._alive:
                return
            tree = self.hdc_tree
            try:
                if not tree.winfo_exists():
                    return
                self.safe_tree_delete(tree, *tree.get_children())

                error_items = [d for d in devices if "error" in d]
                if error_items:
                    for item in error_items:
                        self.safe_tree_insert(tree, "", "end", text=item["error"], tags=("error",))
                    return

                if not devices:
                    self.safe_tree_insert(tree, "", "end", text="未检测到已连接的设备", tags=("error",))
                    return

                connected_count = sum(1 for d in devices if str(d.get("status", "")).lower() == "connected")
                group_text = f"已发现设备 ({len(devices)}), 已连接 ({connected_count})"
                device_group = self.safe_tree_insert(tree, "", "end", text=group_text, open=True)

                for device in devices:
                    status = str(device.get("status", "未知"))
                    sn = device.get("sn", "未知SN")
                    status_tag = "ok" if status.lower() == "connected" else "error"
                    node = self.safe_tree_insert(
                        tree,
                        device_group,
                        "end",
                        text=f"{sn} ({status})",
                        tags=(status_tag,),
                        values=(sn,),
                    )
                    self.safe_tree_insert(tree, node, "end", text=f"类型: {device.get('type', '未知')}", tags=("version",))
                    self.safe_tree_insert(tree, node, "end", text=f"详情: {device.get('details', '无')}", tags=("path",))
            except tk.TclError:
                pass

        threading.Thread(target=task, daemon=True).start()

    def on_usb_change(self):
        # 在这里处理 USB 状态变化的逻辑
        print("harmonyOS_CheckToolPage: USB 状态发生变化")
        self._fill_hdc_tree()  # 刷新设备列表
    # ------------------------------------------------------------------
    # HAP 安装流程 (UI 部分)
    # ------------------------------------------------------------------
    def _show_hdc_context_menu(self, event):
        item_id = self.hdc_tree.identify_row(event.y)
        if not item_id: return

        tags = self.hdc_tree.item(item_id, "tags")
        # 只为状态为 "ok" (Connected) 的设备显示菜单
        if tags and tags[0] == "ok":
            device_sn = self.hdc_tree.item(item_id, "values")[0]
            self.hdc_tree.selection_set(item_id)

            menu = tk.Menu(self, tearoff=0)
            menu.add_command(label=f"向 {device_sn[:12]}... 安装 HAP",
                             command=lambda sn=device_sn: self._trigger_hap_install(sn))
            menu.post(event.x_root, event.y_root)

    def _trigger_hap_install(self, device_sn):
        """UI流程第1步: 弹出文件选择框"""
        hap_path = filedialog.askopenfilename(title=f"为设备 {device_sn} 选择 HAP 文件",
                                              filetypes=[("HAP files", "*.hap")])
        if not hap_path: return
        threading.Thread(target=self._background_install_task, args=(device_sn, hap_path), daemon=True).start()

    def _background_install_task(self, device_sn, hap_path):
        """UI流程第2步: 在后台调用逻辑函数"""
        if not self._alive: return
        result = install_hap_package(device_sn, hap_path)
        if not self._alive: return
        self.after(0, self._show_install_result, result)

    def _show_install_result(self, result):
        """UI流程第3步: 在主线程显示最终结果"""
        messagebox.showinfo("安装结果", result)

    # ------------------------------------------------------------------
    # 打包流程 (UI 部分)
    # ------------------------------------------------------------------
    def build_project(self):
        path = self.project_root.get().strip()
        if not is_valid_project_root(path):
            messagebox.showwarning("路径无效", "请选择有效的 HarmonyOS 项目根目录 (必须包含 hvigorfile.ts 等文件)。")
            return

        # 创建打包日志窗口
        log_window = tk.Toplevel(self)
        log_window.title(f"正在打包 - {os.path.basename(path)}")
        log_window.geometry("600x400")
        log_window.grab_set()
        log_window.resizable(True, True)

        log_text = tk.Text(log_window, state="disabled", wrap=tk.WORD, bg="#f0f0f0")
        log_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        def log_to_widget(message):
            """线程安全的日志记录函数"""
            if not self._alive or not log_text.winfo_exists(): return
            log_text.configure(state="normal")
            log_text.insert("end", message + "\n")
            log_text.configure(state="disabled")
            log_text.see("end")

        def task():
            result = run_packing_process(path, lambda msg: self.after(0, log_to_widget, msg))
            if not self._alive: return
            self.after(0, handle_build_result, result)

        def handle_build_result(result):
            if log_window.winfo_exists(): log_window.destroy()
            if result["errors"]:
                messagebox.showerror("打包失败", f"打包过程中发生错误:\n\n{result['errors'][0]}")
            elif result["hap_files"]:
                messagebox.showinfo("打包成功", f"打包成功！\n\n已生成 HAP 文件:\n{result['hap_files'][0]}")
            else:
                messagebox.showerror("打包失败", "打包过程已完成，但未在预期目录找到 .hap 文件，请检查日志。")

        threading.Thread(target=task, daemon=True).start()

    # ------------------------------------------------------------------
    # 导出报告
    # ------------------------------------------------------------------
    def export_report(self):
        path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files", "*.txt")],
                                            title="导出检测报告")
        if not path: return
        try:
            now = time.strftime('%Y-%m-%d %H:%M:%S')
            content = [
                f"HarmonyOS 开发环境检测报告 - {now}",
                "\n" + "=" * 30,
                "【DevEco Studio】",
                *self._get_tree_text(self.deco_tree),
                "\n" + "=" * 30,
                "【环境依赖】",
                *self._get_tree_text(self.env_tree),
                "\n" + "=" * 30,
                "【HDC 设备】",
                *self._get_tree_text(self.hdc_tree),
            ]
            with open(path, "w", encoding="utf-8") as f:
                f.write("\n".join(content))
            messagebox.showinfo("导出成功", f"报告已成功保存至:\n{path}")
        except Exception as e:
            messagebox.showerror("导出失败", f"导出报告时发生错误: {e}")

    def _get_tree_text(self, tree):
        """辅助函数，将 Treeview 内容转换为文本列表"""
        lines = []
        for top_item in tree.get_children(""):
            def walk(node, indent=""):
                text = tree.item(node, "text")
                lines.append(f"{indent}- {text}")
                for child_item in tree.get_children(node):
                    walk(child_item, indent + "  ")

            walk(top_item)
        return lines if lines else ["  (无内容)"]

    def safe_tree_insert(self, tree, parent, index, text, tags=None, values=None, open=False):
        """线程安全地向 Treeview 插入节点"""
        try:
            if tree.winfo_exists():
                return tree.insert(parent, index, text=text, tags=tags or (), values=values or (), open=open)
        except tk.TclError:
            return None

    def safe_tree_delete(self, tree_widget, *items):
        """安全删除树状视图中的项目"""
        if not tree_widget or not tree_widget.winfo_exists():
            return
        try:
            tree_widget.delete(*items)
        except tk.TclError as e:
            if "invalid command name" not in str(e):
                logging.error(f"删除树项目失败: {e}")
